package com.example.student.service;

import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
@Service
public class RtspService {

    @Value("${rtsp.hls.output.directory}")
    private String hlsOutputDirectory;

    private final Map<String, StreamProcessor> activeStreams = new ConcurrentHashMap<>();
    private ExecutorService executorService;

    @PostConstruct
    public void init() {
        executorService = Executors.newCachedThreadPool();
        createHlsDirectory();
    }

    private void createHlsDirectory() {
        try {
            Path path = Paths.get(hlsOutputDirectory);
            if (!Files.exists(path)) {
                Files.createDirectories(path);
                log.info("Created HLS output directory: {}", hlsOutputDirectory);
            }
            log.info("HLS目录绝对路径: {}", path.toAbsolutePath().toString());
        } catch (IOException e) {
            log.error("Failed to create HLS output directory", e);
        }
    }

    public String startStream(String rtspUrl) {
        String streamId = generateStreamId(rtspUrl);

        if (activeStreams.containsKey(streamId)) {
            log.info("Stream already active: {}", streamId);
            return streamId;
        }

        try {
            StreamProcessor processor = new StreamProcessor(rtspUrl, streamId);
            activeStreams.put(streamId, processor);
            executorService.submit(processor);
            log.info("Started stream: {} with ID: {}", rtspUrl, streamId);
            return streamId;
        } catch (Exception e) {
            log.error("Failed to start stream: {}", rtspUrl, e);
            throw new RuntimeException("Failed to start RTSP stream", e);
        }
    }

    public void stopStream(String streamId) {
        StreamProcessor processor = activeStreams.get(streamId);
        if (processor != null) {
            processor.stop();
            activeStreams.remove(streamId);
            log.info("Stopped stream: {}", streamId);
        }
    }

    public boolean isStreamActive(String streamId) {
        return activeStreams.containsKey(streamId);
    }

    private String generateStreamId(String rtspUrl) {
        return Integer.toHexString(rtspUrl.hashCode());
    }

    private class StreamProcessor implements Runnable {
        private final String rtspUrl;
        private final String streamId;
        private volatile boolean running = true;

        public StreamProcessor(String rtspUrl, String streamId) {
            this.rtspUrl = rtspUrl;
            this.streamId = streamId;
        }

        @Override
        public void run() {
            FFmpegFrameGrabber grabber = null;
            FFmpegFrameRecorder recorder = null;

            try {
                // Configure grabber for RTSP stream
                grabber = new FFmpegFrameGrabber(rtspUrl);
                grabber.setOption("rtsp_transport", "tcp"); // Use TCP for RTSP
                grabber.setOption("stimeout", "5000000"); // 5 second timeout
                grabber.start();

                log.info("Connected to RTSP stream: {}", rtspUrl);

                // 获取HLS输出目录的绝对路径
                Path outputDirPath = Paths.get(hlsOutputDirectory).toAbsolutePath();
                String outputPath = outputDirPath.toString() + File.separator + streamId;
                log.info("HLS输出文件路径: {}", outputPath + ".m3u8");

                // Configure HLS recorder
                recorder = new FFmpegFrameRecorder(
                        outputPath + ".m3u8",
                        grabber.getImageWidth(),
                        grabber.getImageHeight(),
                        grabber.getAudioChannels()
                );

                // Configure HLS output format
                recorder.setFormat("hls");
                recorder.setOption("hls_time", "2"); // Segment duration in seconds
                recorder.setOption("hls_list_size", "10"); // Number of segments in playlist
                recorder.setOption("hls_flags", "delete_segments"); // Delete old segments
                recorder.setOption("hls_segment_filename", outputPath + "-%03d.ts");

                recorder.setFrameRate(grabber.getFrameRate());
                recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
                recorder.setVideoOption("preset", "ultrafast");
                recorder.setVideoOption("tune", "zerolatency");

                if (grabber.getAudioChannels() > 0) {
                    recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
                    recorder.setSampleRate(grabber.getSampleRate());
                }

                recorder.start();

                log.info("Started HLS recorder for stream: {}", streamId);

                // Process frames
                Frame frame;
                while (running && (frame = grabber.grab()) != null) {
                    recorder.record(frame);
                }
            } catch (Exception e) {
                log.error("Error processing RTSP stream: {}", rtspUrl, e);
            } finally {
                // Clean up
                try {
                    if (recorder != null) {
                        recorder.stop();
                        recorder.release();
                    }
                    if (grabber != null) {
                        grabber.stop();
                        grabber.release();
                    }
                    log.info("Cleaned up resources for stream: {}", streamId);
                } catch (Exception e) {
                    log.error("Error cleaning up resources", e);
                }

                // Remove from active streams if still there
                if (running) {
                    activeStreams.remove(streamId);
                    log.info("Stream stopped due to error or end: {}", streamId);
                }
            }
        }

        public void stop() {
            running = false;
        }
    }
} 